<!DOCTYPE html>
<html>
<head>
    <title>智能表单验证器</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .form-container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        
        .form-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }
        
        .form-header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        
        .form-header p {
            opacity: 0.9;
            font-size: 1.1em;
        }
        
        .form-body {
            padding: 40px;
        }
        
        .form-group {
            margin-bottom: 25px;
            position: relative;
        }
        
        .form-group label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #333;
            font-size: 1.1em;
        }
        
        .form-group input,
        .form-group select,
        .form-group textarea {
            width: 100%;
            padding: 15px;
            border: 2px solid #e1e5e9;
            border-radius: 8px;
            font-size: 16px;
            transition: all 0.3s ease;
            background: #f8f9fa;
        }
        
        .form-group input:focus,
        .form-group select:focus,
        .form-group textarea:focus {
            outline: none;
            border-color: #667eea;
            background: white;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }
        
        .form-group.error input,
        .form-group.error select,
        .form-group.error textarea {
            border-color: #e74c3c;
            background: #fdf2f2;
        }
        
        .form-group.success input,
        .form-group.success select,
        .form-group.success textarea {
            border-color: #27ae60;
            background: #f2fdf2;
        }
        
        .error-message {
            color: #e74c3c;
            font-size: 14px;
            margin-top: 5px;
            display: flex;
            align-items: center;
            animation: slideIn 0.3s ease;
        }
        
        .success-message {
            color: #27ae60;
            font-size: 14px;
            margin-top: 5px;
            display: flex;
            align-items: center;
            animation: slideIn 0.3s ease;
        }
        
        @keyframes slideIn {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        .validation-icon {
            position: absolute;
            right: 15px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 20px;
        }
        
        .validation-icon.success {
            color: #27ae60;
        }
        
        .validation-icon.error {
            color: #e74c3c;
        }
        
        .form-actions {
            display: flex;
            gap: 15px;
            margin-top: 30px;
        }
        
        .btn {
            flex: 1;
            padding: 15px;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }
        
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
        }
        
        .btn-secondary {
            background: #6c757d;
            color: white;
        }
        
        .btn-secondary:hover {
            background: #5a6268;
            transform: translateY(-2px);
        }
        
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
        }
        
        .progress-bar {
            height: 4px;
            background: #e1e5e9;
            border-radius: 2px;
            margin: 20px 0;
            overflow: hidden;
        }
        
        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea, #764ba2);
            border-radius: 2px;
            transition: width 0.3s ease;
        }
        
        .shortcuts-info {
            background: #f8f9fa;
            border: 1px solid #e1e5e9;
            border-radius: 8px;
            padding: 15px;
            margin-top: 20px;
        }
        
        .shortcuts-info h4 {
            margin-bottom: 10px;
            color: #333;
        }
        
        .shortcut-item {
            display: flex;
            justify-content: space-between;
            margin: 5px 0;
            font-size: 14px;
        }
        
        .shortcut-key {
            background: #e1e5e9;
            padding: 2px 8px;
            border-radius: 4px;
            font-family: monospace;
        }
        
        .status-panel {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 20px;
            margin-top: 20px;
        }
        
        .status-item {
            display: flex;
            justify-content: space-between;
            margin: 10px 0;
            padding: 10px;
            background: white;
            border-radius: 5px;
            border-left: 4px solid #667eea;
        }
        
        .toast {
            position: fixed;
            top: 20px;
            right: 20px;
            background: #27ae60;
            color: white;
            padding: 15px 20px;
            border-radius: 8px;
            box-shadow: 0 10px 20px rgba(0,0,0,0.1);
            animation: slideInRight 0.3s ease;
            z-index: 1000;
        }
        
        .toast.error {
            background: #e74c3c;
        }
        
        @keyframes slideInRight {
            from {
                opacity: 0;
                transform: translateX(100%);
            }
            to {
                opacity: 1;
                transform: translateX(0);
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="form-container">
            <div class="form-header">
                <h1>智能表单验证器</h1>
                <p>体验Vue事件处理的强大功能</p>
            </div>
            
            <div class="form-body">
                <!-- 进度条 -->
                <div class="progress-bar">
                    <div class="progress-fill" :style="{width: validationProgress + '%'}"></div>
                </div>
                <p style="text-align: center; color: #666; margin-bottom: 20px;">
                    验证进度: {{ validationProgress }}%
                </p>
                
                <form @submit.prevent="handleSubmit" @keydown="handleKeyboardShortcuts">
                    <!-- 用户名 -->
                    <div class="form-group" :class="getFieldClass('username')">
                        <label for="username">用户名 *</label>
                        <input 
                            id="username"
                            v-model="form.username"
                            @input="validateField('username')"
                            @blur="validateField('username')"
                            @focus="clearFieldError('username')"
                            type="text"
                            placeholder="请输入用户名（3-20个字符）"
                            :aria-describedby="errors.username ? 'username-error' : null"
                        >
                        <div v-if="validationStatus.username" class="validation-icon" :class="validationStatus.username">
                            {{ validationStatus.username === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.username" id="username-error" class="error-message">
                            ⚠️ {{ errors.username }}
                        </div>
                        <div v-else-if="validationStatus.username === 'success'" class="success-message">
                            ✓ 用户名格式正确
                        </div>
                    </div>
                    
                    <!-- 邮箱 -->
                    <div class="form-group" :class="getFieldClass('email')">
                        <label for="email">邮箱地址 *</label>
                        <input 
                            id="email"
                            v-model="form.email"
                            @input="validateField('email')"
                            @blur="validateField('email')"
                            @focus="clearFieldError('email')"
                            type="email"
                            placeholder="请输入邮箱地址"
                            :aria-describedby="errors.email ? 'email-error' : null"
                        >
                        <div v-if="validationStatus.email" class="validation-icon" :class="validationStatus.email">
                            {{ validationStatus.email === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.email" id="email-error" class="error-message">
                            ⚠️ {{ errors.email }}
                        </div>
                        <div v-else-if="validationStatus.email === 'success'" class="success-message">
                            ✓ 邮箱格式正确
                        </div>
                    </div>
                    
                    <!-- 密码 -->
                    <div class="form-group" :class="getFieldClass('password')">
                        <label for="password">密码 *</label>
                        <input 
                            id="password"
                            v-model="form.password"
                            @input="validateField('password')"
                            @blur="validateField('password')"
                            @focus="clearFieldError('password')"
                            type="password"
                            placeholder="请输入密码（至少8位，包含字母和数字）"
                            :aria-describedby="errors.password ? 'password-error' : null"
                        >
                        <div v-if="validationStatus.password" class="validation-icon" :class="validationStatus.password">
                            {{ validationStatus.password === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.password" id="password-error" class="error-message">
                            ⚠️ {{ errors.password }}
                        </div>
                        <div v-else-if="validationStatus.password === 'success'" class="success-message">
                            ✓ 密码强度良好
                        </div>
                    </div>
                    
                    <!-- 确认密码 -->
                    <div class="form-group" :class="getFieldClass('confirmPassword')">
                        <label for="confirmPassword">确认密码 *</label>
                        <input 
                            id="confirmPassword"
                            v-model="form.confirmPassword"
                            @input="validateField('confirmPassword')"
                            @blur="validateField('confirmPassword')"
                            @focus="clearFieldError('confirmPassword')"
                            type="password"
                            placeholder="请再次输入密码"
                            :aria-describedby="errors.confirmPassword ? 'confirmPassword-error' : null"
                        >
                        <div v-if="validationStatus.confirmPassword" class="validation-icon" :class="validationStatus.confirmPassword">
                            {{ validationStatus.confirmPassword === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.confirmPassword" id="confirmPassword-error" class="error-message">
                            ⚠️ {{ errors.confirmPassword }}
                        </div>
                        <div v-else-if="validationStatus.confirmPassword === 'success'" class="success-message">
                            ✓ 密码确认正确
                        </div>
                    </div>
                    
                    <!-- 年龄 -->
                    <div class="form-group" :class="getFieldClass('age')">
                        <label for="age">年龄</label>
                        <input 
                            id="age"
                            v-model.number="form.age"
                            @input="validateField('age')"
                            @blur="validateField('age')"
                            @focus="clearFieldError('age')"
                            type="number"
                            min="1"
                            max="120"
                            placeholder="请输入年龄（1-120）"
                            :aria-describedby="errors.age ? 'age-error' : null"
                        >
                        <div v-if="validationStatus.age" class="validation-icon" :class="validationStatus.age">
                            {{ validationStatus.age === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.age" id="age-error" class="error-message">
                            ⚠️ {{ errors.age }}
                        </div>
                        <div v-else-if="validationStatus.age === 'success'" class="success-message">
                            ✓ 年龄有效
                        </div>
                    </div>
                    
                    <!-- 性别 -->
                    <div class="form-group" :class="getFieldClass('gender')">
                        <label for="gender">性别</label>
                        <select 
                            id="gender"
                            v-model="form.gender"
                            @change="validateField('gender')"
                            @focus="clearFieldError('gender')"
                            :aria-describedby="errors.gender ? 'gender-error' : null"
                        >
                            <option value="">请选择性别</option>
                            <option value="male">男</option>
                            <option value="female">女</option>
                            <option value="other">其他</option>
                        </select>
                        <div v-if="validationStatus.gender" class="validation-icon" :class="validationStatus.gender">
                            {{ validationStatus.gender === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.gender" id="gender-error" class="error-message">
                            ⚠️ {{ errors.gender }}
                        </div>
                        <div v-else-if="validationStatus.gender === 'success'" class="success-message">
                            ✓ 已选择性别
                        </div>
                    </div>
                    
                    <!-- 个人简介 -->
                    <div class="form-group" :class="getFieldClass('bio')">
                        <label for="bio">个人简介</label>
                        <textarea 
                            id="bio"
                            v-model="form.bio"
                            @input="validateField('bio')"
                            @blur="validateField('bio')"
                            @focus="clearFieldError('bio')"
                            rows="4"
                            placeholder="请简单介绍一下自己（可选，最多500字）"
                            :aria-describedby="errors.bio ? 'bio-error' : null"
                        ></textarea>
                        <div v-if="validationStatus.bio" class="validation-icon" :class="validationStatus.bio">
                            {{ validationStatus.bio === 'success' ? '✓' : '✗' }}
                        </div>
                        <div v-if="errors.bio" id="bio-error" class="error-message">
                            ⚠️ {{ errors.bio }}
                        </div>
                        <div v-else-if="form.bio && validationStatus.bio === 'success'" class="success-message">
                            ✓ 简介长度合适 ({{ form.bio.length }}/500)
                        </div>
                    </div>
                    
                    <!-- 表单操作 -->
                    <div class="form-actions">
                        <button 
                            type="button" 
                            class="btn btn-secondary"
                            @click="resetForm"
                            :disabled="isSubmitting"
                        >
                            重置表单 (Ctrl+R)
                        </button>
                        <button 
                            type="submit" 
                            class="btn btn-primary"
                            :disabled="!isFormValid || isSubmitting"
                        >
                            {{ isSubmitting ? '提交中...' : '提交表单 (Ctrl+Enter)' }}
                        </button>
                    </div>
                </form>
                
                <!-- 快捷键说明 -->
                <div class="shortcuts-info">
                    <h4>⌨️ 键盘快捷键</h4>
                    <div class="shortcut-item">
                        <span>提交表单</span>
                        <span class="shortcut-key">Ctrl + Enter</span>
                    </div>
                    <div class="shortcut-item">
                        <span>重置表单</span>
                        <span class="shortcut-key">Ctrl + R</span>
                    </div>
                    <div class="shortcut-item">
                        <span>验证所有字段</span>
                        <span class="shortcut-key">Ctrl + Shift + V</span>
                    </div>
                    <div class="shortcut-item">
                        <span>清除所有错误</span>
                        <span class="shortcut-key">Ctrl + Shift + C</span>
                    </div>
                </div>
                
                <!-- 状态面板 -->
                <div class="status-panel">
                    <h4>📊 表单状态</h4>
                    <div class="status-item">
                        <span>表单有效性</span>
                        <span :style="{color: isFormValid ? '#27ae60' : '#e74c3c'}">
                            {{ isFormValid ? '✓ 有效' : '✗ 无效' }}
                        </span>
                    </div>
                    <div class="status-item">
                        <span>已验证字段</span>
                        <span>{{ validatedFieldsCount }}/{{ totalFieldsCount }}</span>
                    </div>
                    <div class="status-item">
                        <span>错误数量</span>
                        <span :style="{color: errorCount > 0 ? '#e74c3c' : '#27ae60'}">
                            {{ errorCount }}
                        </span>
                    </div>
                    <div class="status-item">
                        <span>提交次数</span>
                        <span>{{ submitCount }}</span>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- Toast通知 -->
        <div v-if="toast.show" :class="['toast', toast.type]">
            {{ toast.message }}
        </div>
    </div>

    <script>
        const { createApp, ref, reactive, computed, watch, nextTick } = Vue;
        
        createApp({
            setup() {
                // 表单数据
                const form = reactive({
                    username: '',
                    email: '',
                    password: '',
                    confirmPassword: '',
                    age: null,
                    gender: '',
                    bio: ''
                });
                
                // 错误信息
                const errors = reactive({});
                
                // 验证状态
                const validationStatus = reactive({});
                
                // 其他状态
                const isSubmitting = ref(false);
                const submitCount = ref(0);
                const toast = reactive({
                    show: false,
                    message: '',
                    type: 'success'
                });
                
                // 验证规则
                const validationRules = {
                    username: {
                        required: true,
                        minLength: 3,
                        maxLength: 20,
                        pattern: /^[a-zA-Z0-9_]+$/,
                        message: '用户名必须是3-20位字母、数字或下划线'
                    },
                    email: {
                        required: true,
                        pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
                        message: '请输入有效的邮箱地址'
                    },
                    password: {
                        required: true,
                        minLength: 8,
                        pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/,
                        message: '密码至少8位，必须包含字母和数字'
                    },
                    confirmPassword: {
                        required: true,
                        custom: (value) => value === form.password,
                        message: '两次输入的密码不一致'
                    },
                    age: {
                        required: false,
                        min: 1,
                        max: 120,
                        message: '年龄必须在1-120之间'
                    },
                    gender: {
                        required: false,
                        message: '请选择性别'
                    },
                    bio: {
                        required: false,
                        maxLength: 500,
                        message: '个人简介不能超过500字'
                    }
                };
                
                // 验证单个字段
                const validateField = (fieldName) => {
                    const value = form[fieldName];
                    const rule = validationRules[fieldName];
                    
                    if (!rule) return true;
                    
                    // 清除之前的错误
                    delete errors[fieldName];
                    validationStatus[fieldName] = null;
                    
                    // 必填验证
                    if (rule.required && (!value || value.toString().trim() === '')) {
                        errors[fieldName] = `${getFieldLabel(fieldName)}是必填项`;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    // 如果不是必填且为空，则跳过其他验证
                    if (!rule.required && (!value || value.toString().trim() === '')) {
                        validationStatus[fieldName] = null;
                        return true;
                    }
                    
                    // 长度验证
                    if (rule.minLength && value.length < rule.minLength) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    if (rule.maxLength && value.length > rule.maxLength) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    // 数值范围验证
                    if (rule.min !== undefined && value < rule.min) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    if (rule.max !== undefined && value > rule.max) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    // 正则验证
                    if (rule.pattern && !rule.pattern.test(value)) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    // 自定义验证
                    if (rule.custom && !rule.custom(value)) {
                        errors[fieldName] = rule.message;
                        validationStatus[fieldName] = 'error';
                        return false;
                    }
                    
                    // 验证通过
                    validationStatus[fieldName] = 'success';
                    return true;
                };
                
                // 获取字段标签
                const getFieldLabel = (fieldName) => {
                    const labels = {
                        username: '用户名',
                        email: '邮箱',
                        password: '密码',
                        confirmPassword: '确认密码',
                        age: '年龄',
                        gender: '性别',
                        bio: '个人简介'
                    };
                    return labels[fieldName] || fieldName;
                };
                
                // 获取字段样式类
                const getFieldClass = (fieldName) => {
                    if (errors[fieldName]) return 'error';
                    if (validationStatus[fieldName] === 'success') return 'success';
                    return '';
                };
                
                // 清除字段错误
                const clearFieldError = (fieldName) => {
                    if (errors[fieldName]) {
                        delete errors[fieldName];
                        validationStatus[fieldName] = null;
                    }
                };
                
                // 验证所有字段
                const validateAllFields = () => {
                    let isValid = true;
                    Object.keys(validationRules).forEach(fieldName => {
                        if (!validateField(fieldName)) {
                            isValid = false;
                        }
                    });
                    return isValid;
                };
                
                // 重置表单
                const resetForm = () => {
                    Object.keys(form).forEach(key => {
                        if (typeof form[key] === 'number') {
                            form[key] = null;
                        } else {
                            form[key] = '';
                        }
                    });
                    Object.keys(errors).forEach(key => {
                        delete errors[key];
                    });
                    Object.keys(validationStatus).forEach(key => {
                        validationStatus[key] = null;
                    });
                    showToast('表单已重置', 'success');
                };
                
                // 清除所有错误
                const clearAllErrors = () => {
                    Object.keys(errors).forEach(key => {
                        delete errors[key];
                    });
                    Object.keys(validationStatus).forEach(key => {
                        validationStatus[key] = null;
                    });
                    showToast('所有错误已清除', 'success');
                };
                
                // 处理表单提交
                const handleSubmit = async () => {
                    if (!validateAllFields()) {
                        showToast('请修正表单错误后再提交', 'error');
                        return;
                    }
                    
                    isSubmitting.value = true;
                    
                    try {
                        // 模拟API调用
                        await new Promise(resolve => setTimeout(resolve, 2000));
                        
                        submitCount.value++;
                        showToast('表单提交成功！', 'success');
                        
                        // 可以选择重置表单或保留数据
                        // resetForm();
                    } catch (error) {
                        showToast('提交失败，请重试', 'error');
                    } finally {
                        isSubmitting.value = false;
                    }
                };
                
                // 处理键盘快捷键
                const handleKeyboardShortcuts = (event) => {
                    // Ctrl+Enter: 提交表单
                    if (event.ctrlKey && event.key === 'Enter') {
                        event.preventDefault();
                        if (isFormValid.value && !isSubmitting.value) {
                            handleSubmit();
                        }
                        return;
                    }
                    
                    // Ctrl+R: 重置表单
                    if (event.ctrlKey && event.key === 'r') {
                        event.preventDefault();
                        resetForm();
                        return;
                    }
                    
                    // Ctrl+Shift+V: 验证所有字段
                    if (event.ctrlKey && event.shiftKey && event.key === 'V') {
                        event.preventDefault();
                        validateAllFields();
                        showToast('已验证所有字段', 'success');
                        return;
                    }
                    
                    // Ctrl+Shift+C: 清除所有错误
                    if (event.ctrlKey && event.shiftKey && event.key === 'C') {
                        event.preventDefault();
                        clearAllErrors();
                        return;
                    }
                };
                
                // 显示Toast通知
                const showToast = (message, type = 'success') => {
                    toast.message = message;
                    toast.type = type;
                    toast.show = true;
                    
                    setTimeout(() => {
                        toast.show = false;
                    }, 3000);
                };
                
                // 计算属性
                const isFormValid = computed(() => {
                    // 检查必填字段
                    const requiredFields = Object.keys(validationRules).filter(
                        key => validationRules[key].required
                    );
                    
                    for (const field of requiredFields) {
                        if (!form[field] || form[field].toString().trim() === '') {
                            return false;
                        }
                        if (errors[field]) {
                            return false;
                        }
                    }
                    
                    // 检查所有字段的错误
                    return Object.keys(errors).length === 0;
                });
                
                const errorCount = computed(() => {
                    return Object.keys(errors).length;
                });
                
                const validatedFieldsCount = computed(() => {
                    return Object.values(validationStatus).filter(status => status === 'success').length;
                });
                
                const totalFieldsCount = computed(() => {
                    return Object.keys(validationRules).length;
                });
                
                const validationProgress = computed(() => {
                    if (totalFieldsCount.value === 0) return 0;
                    return Math.round((validatedFieldsCount.value / totalFieldsCount.value) * 100);
                });
                
                // 监听表单变化，自动验证确认密码
                watch(() => form.password, () => {
                    if (form.confirmPassword) {
                        validateField('confirmPassword');
                    }
                });
                
                return {
                    form,
                    errors,
                    validationStatus,
                    isSubmitting,
                    submitCount,
                    toast,
                    isFormValid,
                    errorCount,
                    validatedFieldsCount,
                    totalFieldsCount,
                    validationProgress,
                    validateField,
                    getFieldClass,
                    clearFieldError,
                    validateAllFields,
                    resetForm,
                    clearAllErrors,
                    handleSubmit,
                    handleKeyboardShortcuts,
                    showToast
                };
            }
        }).mount('#app');
    </script>
</body>
</html>
